#include <sstream>
#include <iomanip>
#include <chrono>
#include <ratio>
#include <string.h>
#include <stdarg.h>
#include "log.h"

Log::Log()
    : log_path_(""), log_file_name_(""), async_log_size_(0),
      count_line_(0), split_lines_(0), log_day_(0), log_buf_(nullptr),
      log_block_queue_(nullptr), is_running_async_(false)
{
}

Log::~Log()
{
    if (async_log_size_)
    {
        is_running_async_ = false;
        if (async_thread_.joinable())
        {
            async_thread_.join();
        }

        if (log_block_queue_ != nullptr)
        {
            log_block_queue_->stop();
            delete log_block_queue_;
        }
    }
    if (log_buf_ != nullptr)
    {
        delete[] log_buf_;
    }

    if (file_.is_open())
    {
        file_.close();
    }
}

bool Log::init(const std::string &log_path, const std::string &log_file_name, int level, int async_log_size, int log_buf_size, int split_lines)
{
    log_path_ = log_path;
    log_file_name_ = log_file_name;
    level_ = level;
    log_buf_size_ = log_buf_size;
    split_lines_ = split_lines;
    async_log_size_ = async_log_size;

    log_buf_ = new char[log_buf_size];
    memset(log_buf_, 0, log_buf_size);

    auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    struct tm *ptm = localtime(&tt);
    char date[256] = {0};
    snprintf(date, 255, "%d-%02d-%02d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday);
    std::string file_name = log_path_ + '/' + date + log_file_name_;

    file_.open(file_name, std::ios::app);
    if (!file_.is_open())
    {
        std::cerr << "Open Log File Error: " << strerror(errno) << std::endl;
        is_running_async_ = false;
    }
    log_day_ = ptm->tm_mday;

    if (async_log_size_)
    {
        is_running_async_ = true;
        log_block_queue_ = new BlockQueue<std::string>(async_log_size_);
        async_thread_ = std::thread(&Log::asyncwritelog, this);
    }
    return true;
}

void Log::writeLog(const char *current_file_name, int line_number, LogLevel level, const char *format, ...)
{
    // 如果日志级别小于设定的级别，则不写入日志
    if (level < LogLevel::Debug || level > LogLevel::Error || level < level_)
    {
        std::cerr << "This level not in Debug-Error" << std::endl;
        return;
    }

    std::string path = current_file_name;
    size_t last_pos = path.find_last_of("/\\");
    if (last_pos != std::string::npos)
    {
        path = path.substr(last_pos + 1);
    }

    char type[16] = {0};
    switch (level)
    {
    case Debug:
        strcpy(type, "[debug]:");
        break;
    case Info:
        strcpy(type, "[info]:");
        break;
    case Warning:
        strcpy(type, "[warning]:");
        break;
    case Critical:
        strcpy(type, "[critical]:");
        break;
    case Error:
        strcpy(type, "[error]:");
        break;
    }

    count_line_++;
    auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    struct tm *ptm = localtime(&tt);

    if (log_day_ != ptm->tm_mday || count_line_ > split_lines_)
    {
        file_.close();

        char date[256] = {0};
        snprintf(date, 255, "%d-%02d-%02d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday);
        std::string file_name = log_path_ + '/' + date + log_file_name_;

        file_.open(file_name, std::ios::app);
        if (!file_.is_open())
        {
            std::cerr << "Open Log File Error: " << strerror(errno) << std::endl;
            return;
        }
        log_day_ = ptm->tm_mday;
        count_line_ = 0;
    }

    va_list valst;
    va_start(valst, format);

    int n = sprintf(log_buf_, "%d-%02d-%02d %02d:%02d:%02d %s file:[%s] line:[%d] ",
                    ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
                    ptm->tm_hour, ptm->tm_min, ptm->tm_sec, type, path.c_str(), line_number);

    int m = vsnprintf(log_buf_ + n, log_buf_size_ - 1 - n, format, valst);

    std::string log_str = log_buf_;
    if (is_running_async_)
    {
        log_block_queue_->push(log_str);
    }
    else
    {
        std::unique_lock<std::mutex> lock(mutex_);
        if (file_.is_open())
        {
            file_ << log_str << std::endl;
        }
    }
    va_end(valst);
}

void Log::flush()
{
    if (file_.is_open())
    {
        file_.flush();
    }
}

void Log::asyncwritelog()
{
    while (true)
    {
        if (!is_running_async_ && !log_block_queue_->size())
        {
            return;
        }
        try
        {
            std::string log_str = log_block_queue_->pop();
            if (file_.is_open() && log_str != "")
            {
                file_ << log_str << std::endl;
                flush();
            }
        }
        catch (const std::exception &e)
        {
        }
    }
}
